//
//  QFLSBMan.swift
//  QFImageMaskDemo
//
//  Created by Anakin chen on 2020/3/13.
//  Copyright © 2020 QF Network Technology. All rights reserved.
//

import UIKit
import CoreGraphics

class QFLSBMan: NSObject {
    
    static let kQFkey = "QianFan"
    static let kLengthBitCount = 1 // 使用 1字节来记录使用的 kLengthBit
    static let kLengthBit = 2 // 默认使用2字节=16bytes来记录内容长度，即最大65535（包含d头部信息），但真正能记录的内容长度需要由载体图片的大小解决。以 iPhone4 最小的分辨率 640 x 960，其能记录的隐体信息数值最大为： 614400，已经远大于 65535，所以在截图是足以，其他被裁剪的截图则还需根据实际大小做判断
    
    enum QFLSBMarkBin {
        case key
        case lengthBit
        case length
        case info
    }
    
    static let markBin: [QFLSBMarkBin] = [.key, .lengthBit, .length, .info]
    
    public class func encode(image: UIImage, info: String) -> UIImage? {
        return QFLSBMan.encode3(image: image, info: info);
    }
    
    public class func decode(image: UIImage) -> String? {
        return QFLSBMan.decode3(image: image);
    }
    
    class func baseEncode(data: UnsafeMutablePointer<UInt8>, infoData: Data, width w: Int, height h: Int) {
        let kQHKeyData = kQFkey.data(using: .utf8)!
        let kQHKeyDataCount = kQHKeyData.count
        let kLenghtCount = kLengthBit
        
        let markCount = kQHKeyDataCount + kLengthBitCount + kLenghtCount + infoData.count
        
        var bStop = false
        var markIndex = -1
        var markIndexText: Int = 0
        var markBin: QFLSBMarkBin?
        var markLengthBitCount = 0
        
        for y in 0..<h {
            if bStop {
                break;
            }
            for x in 0..<w {
                let index = (y * w + x)
                
                let markIndexTemp = index / 8
                if markIndexTemp >= markCount {
                    bStop = true
                    break
                }
                
                if markIndex != markIndexTemp {
                    markIndex = markIndexTemp
                    
                    if markIndex < kQHKeyDataCount {
                        markBin = .key
                        markIndexText = Int(kQHKeyData[markIndex])
                    }
                    else if markIndex < kQHKeyDataCount + kLengthBitCount {
                        markBin = .lengthBit
                        markIndexText = kLengthBit
                    }
                    else if markIndex < kQHKeyDataCount + kLengthBitCount + kLenghtCount {
                        markLengthBitCount += 1
                        markBin = .length
                        if markLengthBitCount == 1 {
                            markIndexText = markCount%256
                        }
                        else if markLengthBitCount == 2 {
                            markIndexText = Int(markCount>>8)
                        }
                        else {
                            bStop = true
                            break
                        }
                    }
                    else if markIndex < markCount {
                        markBin = .info
                        markIndexText = Int(infoData[markIndex - (kQHKeyDataCount + kLengthBitCount + kLenghtCount)])
                    }
                }
                
                if markBin == nil {
                    bStop = true
                    break
                }
                
                let offset = 4 * index
                let red = data[offset+1]
                let redBinary = red % 2
                
                let markIndexTextBinaryIndex = index % 8
                let markIndexTextBinary = Int((markIndexText / (1<<Int(markIndexTextBinaryIndex))) % 2)
                
                if redBinary != markIndexTextBinary {
                    if markIndexTextBinary == 0 {
                        if red == 255 {
                            data[offset+1] = 254
                        }
                        else {
                            data[offset+1] = red + 1
                        }
                    }
                    else {
                        if red == 0 {
                            data[offset+1] = 1
                        }
                        else {
                            data[offset+1] = red - 1
                        }
                    }
                }
            }
        }
    }
    
    class func baseDecode(data: UnsafeMutablePointer<UInt8>, width w: Int, height h: Int) -> (result: Bool, info: String) {
        
        let kQHKeyData = kQFkey.data(using: .utf8)!
        let kQHKeyDataCount = kQHKeyData.count
        
        var jumpMarkCount = 0
        var markCount = 0
        var bStop = false
        var markBinary = 0
        var markData: Data
        var markInt = 0
        var markLengthBit = 0
        var markLengthBitCut = -1
        
        for i in markBin {
            jumpMarkCount = markCount
            
            bStop = false
            markBinary = 0
            
            markData = Data()
            
            switch i {
            case .key:
                markCount += kQHKeyDataCount
            case .lengthBit:
                markCount += kLengthBitCount
            case .length:
                markCount += markLengthBit
            case .info:
                markCount += markInt
                markInt = 0
            }
            
            markInt = 0
            
            for y in 0..<h {
                if bStop {
                    break
                }
                for x in 0..<w {
                    let index = (y * w + x)
                    
                    let markIndexTemp = index / 8
                    if markIndexTemp < jumpMarkCount {
                        continue
                    }
                    
                    if markIndexTemp >= markCount {
                        bStop = false
                        break
                    }
                    
                    let markIndexTextBinaryIndex = index % 8
                    
                    let offset = 4 * index
                    let red = data[offset + 1]
                    let redBinary = red % 2
                    
                    let markBinaryTemp = redBinary * (1<<markIndexTextBinaryIndex)
                    markBinary += Int(markBinaryTemp)
                    
                    if markIndexTextBinaryIndex >= 7 {
                        switch i {
                        case .key:
                            markData.append(UInt8(markBinary))
                        case .lengthBit:
                            markLengthBit = markBinary
                        case .length:
                            markLengthBitCut += 1
                            markInt += Int(markBinary<<markLengthBitCut)
                        case .info:
                            markData.append(UInt8(markBinary))
                        }
                        markBinary = 0
                    }
                }
            }
            
            switch i {
            case .key:
                let key = String(data: markData, encoding: .utf8)
                if key != kQFkey {
                    return (false, "不是加密图片")
                }
            case .lengthBit:
                print("加密内容的长度使用的字节数：\(markLengthBit)")
            case .length:
                markInt = markInt - (kQHKeyDataCount + kLengthBitCount + markLengthBit)
                if markInt <= 0 {
                    return (false, "没有内容")
                }
            case .info:
                if let info = String(data: markData, encoding: .utf8) {
                    return (true, info)
                }
                else {
                    return (false, "解密失败")
                }
            }
        }
        return (false, "解密失败")
    }
}

extension QFLSBMan {

    private class func create_bitmap_context(img: CGImage) -> CGContext? {
        
        // 获取 UIImage 对应的 CGImage 对象
        let imageRef = img
        
        // 获取 宽 和 高
        let width = imageRef.width
        let height = imageRef.height
        
        guard width != 0 || height != 0 else { return nil }
        
        // 使用设备的颜色空间 RGB
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        
        // 判断是否有 alpha 通道
        let alphaInfo = imageRef.alphaInfo
        var hasAlpha = false
        if alphaInfo == .premultipliedLast ||
            alphaInfo == .premultipliedFirst ||
            alphaInfo == .last ||
            alphaInfo == .first {
            hasAlpha = true
        }
        
        // 位图布局信息
        var bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue
        bitmapInfo |= hasAlpha ? CGImageAlphaInfo.premultipliedFirst.rawValue : CGImageAlphaInfo.noneSkipFirst.rawValue
        
        // 创建 context
        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo) else { return nil }
        
        context.draw(imageRef, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(width), height: CGFloat(height)))
        
        return context
        
    }
    
    private class func encode2(image: UIImage, info: String) -> UIImage? {
        if let cgImg = image.cgImage {
            if let context = create_bitmap_context(img: cgImg) {
                
                let data = unsafeBitCast(context.data, to: UnsafeMutablePointer<UInt8>.self)
                let infoData = info.data(using: .utf8)!
                let h = cgImg.height
                let w = cgImg.width
                QFLSBMan.baseEncode(data: data, infoData: infoData, width: w, height: h)
                
                // 获取解码后的 CGImage 对象
                guard let docededImageRef = context.makeImage() else {
                    return nil
                }

                // 返回解码后的 UIImage 对象
                return UIImage(cgImage: docededImageRef, scale: UIScreen.main.scale, orientation: .up)
            }
        }
        return nil
    }
    
    private class func decode2(image: UIImage) -> String? {
        if let cgImg = image.cgImage {
            if let context = create_bitmap_context(img: cgImg) {
                
                let data = unsafeBitCast(context.data, to: UnsafeMutablePointer<UInt8>.self)
                let h = cgImg.height
                let w = cgImg.width
                let (result, info) = QFLSBMan.baseDecode(data: data, width: w, height: h)
                if result {
                    return info
                }
            }
        }
        return nil
    }
}

extension QFLSBMan {
    
    private class func pixelBufferBy(image: CGImage) -> CVPixelBuffer? {
        let options = [kCVPixelBufferCGImageCompatibilityKey: true, kCVPixelBufferCGBitmapContextCompatibilityKey: true, kCVPixelBufferIOSurfacePropertiesKey: Dictionary<String, Any>()
            ] as CFDictionary
        var pxbuffer: CVPixelBuffer!
        
        let imageWidth = image.width
        let imageHeight = image.height
        
        let status = CVPixelBufferCreate(kCFAllocatorDefault, imageWidth, imageHeight, kCVPixelFormatType_32BGRA, options, &pxbuffer)
        if status != kCVReturnSuccess {
            return nil
        }

        let flags = CVPixelBufferLockFlags(rawValue: 0)
        CVPixelBufferLockBaseAddress(pxbuffer, flags)
        let pxdata = CVPixelBufferGetBaseAddress(pxbuffer)
        
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        
        let context = CGContext(data: pxdata, width: imageWidth, height: imageHeight, bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pxbuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
        
        if let _context = context {
            _context.draw(image, in: CGRect.init(x: 0, y: 0, width: imageWidth, height: imageHeight))
        }
        else {
            CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
            return nil
        }

        CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
        return pxbuffer;
    }
    
    private class func imageBy(pxbuffer: CVPixelBuffer) -> UIImage? {
        let flags = CVPixelBufferLockFlags(rawValue: 0)
        CVPixelBufferLockBaseAddress(pxbuffer, flags)
        
        guard let baseAdr = CVPixelBufferGetBaseAddress(pxbuffer) else { return nil }
        let width = CVPixelBufferGetWidth(pxbuffer)
        let heigth = CVPixelBufferGetHeight(pxbuffer)
        let bufferSize = CVPixelBufferGetDataSize(pxbuffer)
        let bytesRerRow = CVPixelBufferGetBytesPerRowOfPlane(pxbuffer, 0)
        
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        guard let provider = CGDataProvider(dataInfo: nil, data: baseAdr, size: bufferSize, releaseData: { (p1, p2, s) in
            
        }) else {
            return nil
        }
        
        guard let cgImage = CGImage(width: width, height: heigth, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesRerRow, space: rgbColorSpace, bitmapInfo: .byteOrder32Little, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else {
            return nil
        }
        let image = UIImage(cgImage: cgImage)
        
        CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
        
        return image
    }
    
    private class func encode3(image: UIImage, info: String) -> UIImage? {
        
        if let cgImage = image.cgImage {
            if let pxbuffer: CVPixelBuffer = QFLSBOCMan.pixelBuffer(from: cgImage).takeUnretainedValue() {
                let flags = CVPixelBufferLockFlags(rawValue: 0)
                CVPixelBufferLockBaseAddress(pxbuffer, flags)
                if let pxAdr = CVPixelBufferGetBaseAddress(pxbuffer) {
                    
                    let data = unsafeBitCast(pxAdr, to: UnsafeMutablePointer<UInt8>.self)
                    let infoData = info.data(using: .utf8)!
                    let w = CVPixelBufferGetHeight(pxbuffer)
                    let h = CVPixelBufferGetWidth(pxbuffer)
                    QFLSBMan.baseEncode(data: data, infoData: infoData, width: w, height: h)
                    
                    let newImg = QFLSBOCMan.image(from: pxbuffer)
                    CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
                    return newImg
                }
                CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
            }
        }
        
        return nil;
    }
    
    private class func decode3(image: UIImage) -> String? {
        if let gcImage = image.cgImage {
            if let pxbuffer = pixelBufferBy(image: gcImage) {
                let flags = CVPixelBufferLockFlags(rawValue: 0)
                CVPixelBufferLockBaseAddress(pxbuffer, flags)
                if let pxAdr = CVPixelBufferGetBaseAddress(pxbuffer) {
                    
                    let data = unsafeBitCast(pxAdr, to: UnsafeMutablePointer<UInt8>.self)
                    let h = CVPixelBufferGetWidth(pxbuffer)
                    let w = CVPixelBufferGetHeight(pxbuffer)
                    let (result, info) = QFLSBMan.baseDecode(data: data, width: w, height: h)
                    if result {
                        return info
                    }
                }
                CVPixelBufferUnlockBaseAddress(pxbuffer, flags)
            }
        }
        return nil;
    }
}
